﻿// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;

namespace Microsoft.Practices.EnterpriseLibrary.Logging.Filters
{
    /// <summary>
    /// Represents a client-side log filter based on message category.  
    /// Either specific categories can be explicitly allowed, or specific categories can be denied.
    /// </summary>
    [ConfigurationElementType(typeof(CategoryFilterData))]
    public class CategoryFilter : LogFilter
    {
        private ICollection<string> categoryFilters;
        private CategoryFilterMode categoryFilterMode;

        /// <summary>
        /// Initializes a new instance with configuration data.
        /// </summary>
        /// <param name="name">Name of this category filter.</param>
        /// <param name="categoryFilters">Categories to be included in the filter.</param>
        /// <param name="categoryFilterMode"><see cref="CategoryFilterMode"/> used to include or exclude category filters.</param>
        public CategoryFilter(string name, ICollection<string> categoryFilters, CategoryFilterMode categoryFilterMode)
            : base(name)
        {
            this.categoryFilters = categoryFilters;
            this.categoryFilterMode = categoryFilterMode;
        }

        /// <summary>
        /// Tests a log entry against the category filters.
        /// </summary>
        /// <param name="log">Log entry to test.</param>
        /// <returns><b>true</b> if the message passes through the filter and should be logged, <b>false</b> otherwise.</returns>
        public override bool Filter(LogEntry log)
        {
            return ShouldLog(log.Categories);
        }

        /// <summary>
        /// Tests a set of categories against the category filters.
        /// </summary>
        /// <param name="categories">The set of categories.</param>
        /// <returns><b>true</b> if the message passes through the filter and should be logged, <b>false</b> otherwise.</returns>
        public bool ShouldLog(IEnumerable<string> categories)
        {
            bool matchDetected = false;

            foreach (string category in categories)
            {
                if (this.CategoryFilters.Contains(category))
                {
                    matchDetected = true;
                    break;
                }
            }

            return ((CategoryFilterMode.AllowAllExceptDenied == this.CategoryFilterMode) && !matchDetected)
                || ((CategoryFilterMode.DenyAllExceptAllowed == this.CategoryFilterMode) && matchDetected);
        }

        /// <summary>
        /// Test a category against the category filters.
        /// </summary>
        /// <param name="category">The category.</param>
        /// <returns><b>true</b> if the category passes through the category filter, <b>false</b> otherwise.</returns>
        /// <remarks>A log entry for an allowed category may be rejected if the log entry has other denied categories
        /// in its categories liset.</remarks>
        public bool ShouldLog(string category)
        {
            return ShouldLog(new string[] { category });
        }

        /// <summary>
        /// Gets the category names to filter.
        /// </summary>
        public ICollection<string> CategoryFilters
        {
            get { return categoryFilters; }
        }

        /// <summary>
        /// Gets the <see cref="CategoryFilterMode"/> to use for filtering.
        /// </summary>
        public CategoryFilterMode CategoryFilterMode
        {
            get { return categoryFilterMode; }
            set { categoryFilterMode = value; }
        }
    }
}
